//============================================================================
// Name    	: irr_3rdcamera.cpp
// Author  	: murugan
// Version 	:
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <irrlicht.h>
#include <iostream>

using namespace irr;
using namespace core;
using namespace scene;
using namespace video;

class MyEventReceiver : public IEventReceiver
{
public:
	virtual bool OnEvent(const SEvent& event)
	{
   	 // Remember the mouse state
   	 if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
   	 {
   		 switch(event.MouseInput.Event)
   		 {
   		 case EMIE_LMOUSE_PRESSED_DOWN:
   			 MouseState.LeftButtonDown = true;
   			 break;

   		 case EMIE_LMOUSE_LEFT_UP:
   			 MouseState.LeftButtonDown = false;
   			 break;

   		 case EMIE_MOUSE_MOVED:
   			 MouseState.Position.X = event.MouseInput.X;
   			 MouseState.Position.Y = event.MouseInput.Y;
   			 break;

   		 default:
   			 // We won't use the wheel
   			 break;
   		 }
   	 }
    	if (event.EventType == irr::EET_KEY_INPUT_EVENT)
        	KeyIsDown[event.KeyInput.Key] = event.KeyInput.PressedDown;
    	return false;

	}

	virtual bool IsKeyDown(EKEY_CODE keyCode) const
	{
    	return KeyIsDown[keyCode];
	}
	// We'll create a struct to record info on the mouse state
   	struct SMouseState
   	{
       	position2di Position;
       	bool LeftButtonDown;
       	SMouseState() : LeftButtonDown(false) { }
   	} MouseState;
       	const SMouseState & GetMouseState(void) const
       	{
           	return MouseState;
       	}

	MyEventReceiver()
	{
    	for (u32 i=0; i<KEY_KEY_CODES_COUNT; ++i)
        	KeyIsDown[i] = false;
	}

private:
	bool KeyIsDown[KEY_KEY_CODES_COUNT];
};

float lastx=0,lasty=0;
float zrot,yrot;

void calcCameraRotation(position2di pos) {

	float diffx = pos.X - lastx; //check the difference between the current x and the last x position
	lastx = pos.X; //set lastx to the current x position

	yrot += (float) diffx; //set the xrot to yrot with the addition of the difference in the x position


	 float diffy=pos.Y-lasty; //check the difference between the current y and the last y position
	 lasty = pos.Y; //set lasty to the current y position
	 zrot -= (float)diffy; //set the xrot to xrot with the addition of the difference in the y position
	/* float angle_limit=40;
	 if(zrot<-angle_limit)
	 zrot=-angle_limit;
	 else if(zrot>angle_limit)
	 zrot=angle_limit;*/
}

vector3df getIrrRelativeTransformation(ISceneNode* node)
{
	matrix4 mat = node->getRelativeTransformation();
	vector3df in(mat[8], mat[9], mat[10]);
	in.normalize();
	return in;
}

void rotateVectorAroundAxis(vector3df & vector, const vector3df & axis, f32 radians)
{
   quaternion MrQuaternion;
   matrix4 MrMatrix;
   (void)MrQuaternion.fromAngleAxis(radians, axis);
   MrQuaternion.getMatrix(MrMatrix);
   MrMatrix.rotateVect(vector);
}

int main()
{
	video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;

	IrrlichtDevice* device = createDevice(driverType,dimension2d<s32>(640, 480));

	if (device == 0)
		return 1;

	video::IVideoDriver* driver = device->getVideoDriver();
	scene::ISceneManager* smgr = device->getSceneManager();
	driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

	IAnimatedMesh* mesh = smgr->getMesh("ninja.b3d");
	IAnimatedMeshSceneNode* node = smgr->addAnimatedMeshSceneNode(mesh);
	node->setMaterialTexture(0, driver->getTexture("nskinrd.jpg"));
	node->setMaterialFlag(EMF_LIGHTING, false);
	node->setFrameLoop(1, 14);
	node->setScale(vector3df(0.5, 0.5, 0.5));

	IMesh* mesh1 = smgr->getMesh("env_big1.b3d");
	IMeshSceneNode* nodeSphere= smgr->addMeshSceneNode(mesh1);
	nodeSphere->setMaterialFlag(video::EMF_LIGHTING, false);

	// add camera
	scene::ICameraSceneNode* camera = smgr->addCameraSceneNode();

	// create event receiver
	MyEventReceiver receiver;
	device->setEventReceiver(&receiver);

	// create skybox
	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);

	smgr->addSkyBoxSceneNode(driver->getTexture("irrlicht2_up.jpg"),
			driver->getTexture("irrlicht2_dn.jpg"),
			driver->getTexture("irrlicht2_lf.jpg"),
			driver->getTexture("irrlicht2_rt.jpg"),
			driver->getTexture("irrlicht2_ft.jpg"),
			driver->getTexture("irrlicht2_bk.jpg"));

	driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);

	node->setPosition(vector3df(0, 0, 0));
	nodeSphere->setPosition(vector3df(0, 0, 0));
	int animationSpeed = 20;
	yrot = 180.0f;
	zrot = 90.0f;
	float speed = .10f;

	f32 orbitDistance = 8.0f;

	int lastyrot = 0.0;
	int lastxrot = 0.0;
	while (device->run())
		if (device->isWindowActive()) {

			vector3df pos = node->getPosition();
			vector3df pos_forcamera = vector3df(pos.X,pos.Y+5,pos.Z);

			position2di posCur = receiver.GetMouseState().Position;
			calcCameraRotation(posCur);

			// Work out the 3 axes for the camera.
			vector3df forward = (camera->getTarget() - camera->getAbsolutePosition()).normalize();
			vector3df up = camera->getUpVector();
			vector3df right = forward.crossProduct(up);

			rotateVectorAroundAxis(forward, up,	((float) ((int) (yrot - lastyrot) % 360)) * DEGTORAD);

			if(yrot - lastyrot==0)
			 rotateVectorAroundAxis(forward, right,  ((float)((int)(zrot-lastxrot)%360))*DEGTORAD);

			// Move BACK up the forward axis of the camera to place it in its orbit.
			camera->setPosition(pos_forcamera - (forward * orbitDistance));

			// Point the camera at the target node, and align its up axis correctly.
			camera->setTarget(pos_forcamera);
			camera->setUpVector(up);

			node->setPosition(vector3df(pos.X,pos.Y,pos.Z));
			node->setRotation(node->getRotation() + vector3df(0.0f, ((int) (yrot - lastyrot) % 360),0.0f));
			vector3df in = getIrrRelativeTransformation(node);
			 pos = node->getPosition();


			 node->setAnimationSpeed(0.0);
			if (receiver.IsKeyDown(KEY_KEY_W)) {
				node->setPosition(pos + in * speed);
				node->setAnimationSpeed(animationSpeed);
			} else if (receiver.IsKeyDown(KEY_KEY_S)) {
				node->setPosition(pos - in * speed);
				node->setAnimationSpeed(animationSpeed);
			} else if (receiver.IsKeyDown(KEY_KEY_D)) {
				vector3df worldUpVector = vector3df(0.0f, 1.0f, 0.0f);
				vector3df camPosition = camera->getPosition();
				vector3df camDirection = camPosition - pos_forcamera;
				vector3df rightVector = worldUpVector.crossProduct(camDirection);
				vector3df camMovement = rightVector.normalize();
				camMovement = camMovement * speed;
				node->setPosition(pos - camMovement);
				node->setAnimationSpeed(animationSpeed);
			} else if (receiver.IsKeyDown(KEY_KEY_A)) {
				 vector3df worldUpVector = vector3df(0.0f, 1.0f, 0.0f);
				 vector3df    camPosition = camera->getPosition();
				 vector3df    camDirection = camPosition - pos_forcamera;
				 vector3df rightVector = worldUpVector.crossProduct(camDirection);
				 vector3df   camMovement = rightVector.normalize();
				 camMovement = camMovement * speed;
				 node->setPosition(pos + camMovement);
				 node->setAnimationSpeed(animationSpeed);
			}

			if (posCur.X > 500)
				device->getCursorControl()->setPosition(130, posCur.Y);
			else if (posCur.X < 125)
				device->getCursorControl()->setPosition(490, posCur.Y);
			if (posCur.Y > 400)
				device->getCursorControl()->setPosition(posCur.X, 30);
			else if (posCur.Y < 25)
				device->getCursorControl()->setPosition(posCur.X, 390);

			lastyrot = yrot;
			lastxrot = zrot;

			driver->beginScene(true, true, 0);
			smgr->drawAll();
			driver->endScene();

   	  device->getCursorControl()->setVisible(false);
			if (receiver.IsKeyDown(KEY_ESCAPE))
				device->closeDevice();
		}

	device->drop();

	return 0;
}



